package drds.server.net;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.StandardSocketOptions;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import drds.server.Server;

/**
 * 
 * @author 曹正辉<330937205@qq.com>
 * 
 */
public final class NewAcceptor extends Thread {
	private static final Logger LOGGER = LoggerFactory.getLogger(NewAcceptor.class);
	private static final Id ID = new Id();

	private final Selector selector;
	private final ServerSocketChannel serverSocketChannel;
	private final int port;
	private final Reactors reactors;
	private long acceptCount;

	public NewAcceptor(String name, String bindIp, int bindPort, SessionContextFactory sessionContextFactory, Reactors reactors) throws IOException {
		super.setName(name);
		this.selector = Selector.open();
		this.serverSocketChannel = ServerSocketChannel.open();
		this.serverSocketChannel.configureBlocking(false);
		/** 设置TCP属性 */
		this.serverSocketChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
		this.serverSocketChannel.setOption(StandardSocketOptions.SO_RCVBUF, 1024 * 16 * 2);
		// backlog=100
		this.serverSocketChannel.bind(new InetSocketAddress(bindIp, bindPort), 100);
		this.port = bindPort;
		this.serverSocketChannel.register(this.selector, SelectionKey.OP_ACCEPT);
		this.reactors = reactors;
	}

	public int getPort() {
		return port;
	}

	public long getAcceptCount() {
		return acceptCount;
	}

	@Override
	public void run() {
		final Selector selector = this.selector;
		for (;;) {
			++acceptCount;
			try {
				selector.select(1000L);
				Set<SelectionKey> selectedKeySet = selector.selectedKeys();
				try {
					for (SelectionKey selectedKey : selectedKeySet) {
						if (selectedKey.isValid() && selectedKey.isAcceptable()) {
							accept();
						} else {
							selectedKey.cancel();
						}
					}
				} finally {
					selectedKeySet.clear();
				}
			} catch (Exception e) {
				LOGGER.error(getName(), e);
			}
		}
	}

	/**
	 * 接受客户端连接
	 */
	private void accept() {
		SocketChannel socketChannel = null;
		try {
			socketChannel = this.serverSocketChannel.accept();
			socketChannel.configureBlocking(false);
			ISessionContext sessionContext = SessionContextFactory.make(socketChannel);
			sessionContext.setId(ID.getId());
			SessionContextGroupShareResources processor = (SessionContextGroupShareResources) Server.getInstance().nextProcessor();
			sessionContext.setProcessor(processor);

			Reactor reactor = this.reactors.getReactor();
			/**
			 * 将客户端连接提交给待注册队列,等待注册到Selector
			 */
			reactor.postRegister(sessionContext);

		} catch (Exception e) {
			LOGGER.error(getName(), e);
			closeSocketChannel(socketChannel);
		}
	}

	private static void closeSocketChannel(SocketChannel socketChannel) {
		if (socketChannel == null) {
			return;
		}
		Socket socket = socketChannel.socket();
		if (socket != null) {
			try {
				socket.close();
			} catch (IOException e) {
				LOGGER.error("closeChannelError", e);
			}
		}
		try {
			socketChannel.close();
		} catch (IOException e) {
			LOGGER.error("closeChannelError", e);
		}
	}

	/**
	 * Socket连接上下文ID生成器
	 */
	private static class Id {

		private static final long MAX_VALUE = 0xffffffffL;

		private long acceptId = 0L;
		private final Object lock = new Object();

		private long getId() {
			synchronized (lock) {
				if (acceptId >= MAX_VALUE) {
					acceptId = 0L;
				}
				return ++acceptId;
			}
		}
	}

}